﻿/**
* CRL
*/
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using System.Net.Sockets;
using System.Threading;
using CRL.Core;
using CRL.Core.Extension;

namespace CRL.RedisProvider
{
    /// <summary>
    /// redis缓存客户端调用类
    /// </summary>
    public class RedisClient
    {
        int _id = -1;
        string _name;
        public RedisClient()
        {
            _id = -1;
        }
        public RedisClient(int db = -1, string name = "")
        {
            _id = db;
            _name = name;
        }
        //internal static Func<string, string> GetRedisConn
        //{
        //    get
        //    {
        //        return ConfigBuilder.GetConfig<Func<string, string>>("redisConn");
        //    }
        //}
        public bool Remove(string key)
        {
            return new StackExchangeRedisHelper(_name, _id).Remove(key);
        }

        public T KGet<T>(string key)
        {
            bool find;
            return KGet<T>(key, out find);
        }
        public T KGet<T>(string key, out bool find)
        {
            var str = KGetString(key, out find);
            if (find)
            {
                return SerializeHelper.DeserializeFromJson<T>(str);
            }
            return default(T);
        }
        public string KGet(string key)
        {
            bool find;
            return KGet<string>(key, out find);
        }
        public Dictionary<string, T> KGetAll<T>(List<string> keys)
        {
            return new StackExchangeRedisHelper(_name, _id).GetAll<T>(keys);
        }
        /// <summary>
        /// 读取Key/Value值
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="key"></param>
        /// <returns></returns>
        string KGetString(string key, out bool find)
        {
            find = false;
            var str = new StackExchangeRedisHelper(_name, _id).Get(key);
            find = str != null;
            return str;
        }
        public List<string> SearchKey(string key, int pageSize = 50)
        {
            return new StackExchangeRedisHelper(_name, _id).SearchKey(key, pageSize);
        }

        #region hash
        public void HSet(string hashId, string key, object obj)
        {
            new StackExchangeRedisHelper(_name, _id).HSet(hashId, key, obj);
        }
        public void HSet(string hashId, Dictionary<string, object> values)
        {
            new StackExchangeRedisHelper(_name, _id).HSet(hashId, values);
        }
        public bool HRemove(string hashId, string key)
        {
            return new StackExchangeRedisHelper(_name, _id).HRemove(hashId, key);
        }

        public T HGet<T>(string hashId, string key)
        {
            var a = new StackExchangeRedisHelper(_name, _id).HGet<T>(hashId, key, out var value);
            return value;
        }
        public bool HGet<T>(string hashId, string key, out T value)
        {
            var a = new StackExchangeRedisHelper(_name, _id).HGet<T>(hashId, key, out value);
            return a;
        }
        public List<T> HGetAll<T>(string hashId)
        {
            return new StackExchangeRedisHelper(_name, _id).HGetAll<T>(hashId);
        }
        public Dictionary<string, T> HGetAll<T>(string hashId, List<string> keys)
        {
            return new StackExchangeRedisHelper(_name, _id).HGetAll<T>(hashId, keys);
        }
        public Dictionary<string, T> HGetDic<T>(string hashId)
        {
            return new StackExchangeRedisHelper(_name, _id).HGetDic<T>(hashId);
        }

        public bool HContainsKey(string hashId, string key)
        {
            return new StackExchangeRedisHelper(_name, _id).HContainsKey(hashId, key);
        }

        public long GetHashCount(string hashId)
        {
            return new StackExchangeRedisHelper(_name, _id).GetHashCount(hashId);
        }
        public List<string> HGetAllKeys(string hashId)
        {
            var allKeys = new StackExchangeRedisHelper(_name, _id).GetDatabase().HashKeys(hashId);
            if (allKeys.Length == 0)
            {
                return new List<string>();
            }
            return allKeys.Select(b => b.ToString()).ToList();
        }
        #endregion
        /// <summary>
        /// 延期
        /// </summary>
        /// <param name="key"></param>
        /// <param name="expiresTime"></param>
        public void KSetEntryIn(string key, TimeSpan expiresTime)
        {
            new StackExchangeRedisHelper(_name, _id).GetDatabase().KeyExpire(key, expiresTime);
        }

        public void KSet(string key, object obj, TimeSpan? timeSpan)
        {
            new StackExchangeRedisHelper(_name, _id).Set(key, obj, timeSpan);
        }

        public bool ContainsKey(string key)
        {
            return new StackExchangeRedisHelper(_name, _id).GetDatabase().KeyExists(key);
        }
        /// <summary>
        /// 递增
        /// </summary>
        /// <param name="key"></param>
        /// <param name="field"></param>
        /// <param name="timeOut"></param>
        /// <returns></returns>
        public long Increment(string key, string field, TimeSpan? timeOut = null, int num = 1)
        {
            key = string.Format("Increment_{0}", key);
            bool exists = false;
            if (timeOut != null)
            {
                exists = ContainsKey(key);
            }
            var result = new StackExchangeRedisHelper(_name, _id).GetDatabase().HashIncrement(key, field, num);
            if (timeOut != null && !exists)
            {
                //自动失效,清理垃圾数据
                KSetEntryIn(key, timeOut.Value);
            }
            return result;
        }
        #region 消息订阅发布
        public void Pubblish<T>(T obj, bool onlyProcess = true)
        {       //只限当前进程
            var process = System.Diagnostics.Process.GetCurrentProcess().Id;
            var channelName = string.Format("{0}_{1}", process, typeof(T).Name);
            if (!onlyProcess)
            {
                channelName = typeof(T).Name;
            }
            string ser = SerializeHelper.SerializerToJson(obj);
            new StackExchangeRedisHelper(_name, _id).Publish(channelName, ser);
        }
        public void Subscribe<T>(Action<T> callBack, bool onlyProcess = true)
        {
            //只限当前进程
            var process = System.Diagnostics.Process.GetCurrentProcess().Id;
            var channelName = string.Format("{0}_{1}", process, typeof(T).Name);
            if (!onlyProcess)
            {
                channelName = typeof(T).Name;
            }
            Console.WriteLine($"Redis Subscribe {channelName}");
            new StackExchangeRedisHelper(_name, _id).Subscribe(channelName, (msg, channel) =>
            {
                var obj = msg.ToObject<T>();
                callBack(obj);
            });
            EventLog.Info($"RedisMessage 启动 {channelName}");
        }
        #endregion

        #region list
        public long ListRightPush<T>(string key, T value)
        {
            return new StackExchangeRedisHelper(_name, _id).ListRightPush(key, value);
        }
        public long ListRightPush(string key, string value)
        {
            return new StackExchangeRedisHelper(_name, _id).ListRightPush(key, value);
        }
        public long ListRemove(string key, object value)
        {
            return new StackExchangeRedisHelper(_name, _id).ListRemove(key, value);
        }
        public List<T> ListRange<T>(string key, long start, long end)
        {
            return new StackExchangeRedisHelper(_name, _id).ListRange<T>(key, start, end);
        }
        public void ListTrim(string key, long start, long end)
        {
            new StackExchangeRedisHelper(_name, _id).ListTrim(key, start, end);
        }
        public long ListLength(string key)
        {
            return new StackExchangeRedisHelper(_name, _id).ListLength(key);
        }
        #endregion

        public bool GetBit(string key, long offSet)
        {
            return new StackExchangeRedisHelper(_name, _id).GetDatabase().StringGetBit(key, offSet);
        }
        public void SetBit(string key, long offSet, bool bit)
        {
            new StackExchangeRedisHelper(_name, _id).GetDatabase().StringSetBit(key, offSet, bit);
        }
        public StackExchange.Redis.IDatabase GetIDatabase()
        {
            var str = "";
            var a = str.GetHashCode();
            return new StackExchangeRedisHelper(_name, _id).GetDatabase();
        }
        public bool BatchRemove(string keyPattern)
        {
            return new StackExchangeRedisHelper(_name, _id).BatchRemove(keyPattern);
        }

        #region geo
        public bool GeoAdd(string key, double longitude, double latitude, string value)
        {
            return new StackExchangeRedisHelper(_name, _id).GetDatabase().GeoAdd(key, longitude, latitude, value);
        }
        public StackExchange.Redis.GeoRadiusResult[] GeoRadius(string key, double longitude, double latitude, double radius)
        {
            return new StackExchangeRedisHelper(_name, _id).GetDatabase().GeoRadius(key, longitude, latitude, radius);
        }
        public bool GeoRemove(string key, string value)
        {
            return new StackExchangeRedisHelper(_name, _id).GetDatabase().GeoRemove(key, value);
        }
        #endregion

        public void AddExpireHashKey(string hashId, string key, DateTime expireTime)
        {
            HSet("ExpireHashKeys", $"{hashId}_{key}", new expireTimeInfo
            {
                hashId = hashId,
                key = key,
                expireTime = expireTime
            });
        }
        public void CheckExpiredHashKeys(int days = 15)
        {
            var all = HGetAll<expireTimeInfo>("ExpireHashKeys");
            var time = DateTime.Now.AddDays(-days);
            var list2 = all.Where(b => b.expireTime < time);
            foreach (var item in list2)
            {
                HRemove(item.hashId, item.key);
                HRemove("ExpireHashKeys", $"{item.hashId}_{item.key}");
            }
            EventLog.Log($"key总数:{all.Count} 过期数:{list2.Count()}", "CheckExpiredHashKeys");
        }
    }
    class expireTimeInfo
    {
        public string hashId;
        public string key;
        public DateTime expireTime;
    }
}
